package models

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

type ResourceRds struct {
	Model // 不用每次写ID 和 createAt了
	ResourceCommon

	Engine            string `json:"Engine" gorm:"comment:类型有 mysql mariadb postgresql"`
	DBInstanceNetType string `json:"DBInstanceNetType" gorm:"comment:实例的网络连接类型，取值：Internet：外网连接 Intranet：内网连接"`
	DBInstanceClass   string `json:"DBInstanceClass" gorm:"comment:实例的规格，取值：rds.mys2.small https://help.aliyun.com/zh/rds/product-overview/primary-apsaradb-rds-instance-types"`
	DBInstanceType    string `json:"DBInstanceType" gorm:"comment:是否主备，Primary：主实例 Readonly：只读实例Guard：灾备实例Temp：临时实例"`
	EngineVersion     string `json:"EngineVersion" gorm:"comment:数据库版本。，取值：8.0 5.7"`
	MasterInstanceId  string `json:"MasterInstanceId" xml:"MasterInstanceId"`
	DBInstanceStatus  string `json:"DBInstanceStatus" xml:"DBInstanceStatus"`
	ReplicateId       string `json:"ReplicateId" xml:"ReplicateId"`

	BindNodes []*StreeNode `json:"bind_nodes" gorm:"many2many:bind_rdss;"` // 多对多
}

// 生成hash值，用于判断是否需要更新
func (rh *ResourceRds) GenHash() string {
	h := md5.New()
	h.Write([]byte(rh.InstanceId))
	h.Write([]byte(rh.DBInstanceClass))
	h.Write([]byte(rh.EngineVersion))
	return hex.EncodeToString(h.Sum(nil))
}

// 添加
func (obj *ResourceRds) Create() error {
	return DB.Create(obj).Error
}

// 删除
func (obj *ResourceRds) DeleteOne() error {
	return DB.Select(clause.Associations).Unscoped().Delete(obj).Error
}

// 根据instance_id删除
func DeleteResourceRdsOneByInstanceId(iid string) error {
	return DB.Select(clause.Associations).Unscoped().Where("instance_id = ?", iid).Delete(&ResourceRds{}).Error
}

// CreateOne 创建
func (obj *ResourceRds) CreateOne() error {
	return DB.Create(obj).Error
}

// UpdateOne 更新
func (obj *ResourceRds) UpdateOne() error {
	return DB.Model(&obj).Updates(obj).Error
}

// UpdateBindNodes 更新绑定节点
func (obj *ResourceRds) UpdateBindNodes(node []*StreeNode) error {
	return DB.Model(&obj).Association("BindNodes").Replace(node)
}

// GetResourceRdsAll 获取所有RDS资源
func GetResourceRdsAll() (objs []*ResourceRds, err error) {
	err = DB.Preload("BindNodes").Find(&objs).Error
	return
}

// GetResourceRdsByLevel 获取指定level的RDS资源
func GetResourceRdsByLevel(level int) (objs []*ResourceRds, err error) {
	err = DB.Where("level = ?", level).Preload("BindNodes").Find(&objs).Error
	return
}

// rds分页限制处理
func GetResourceRdsByIdsWithLimitOffset(ids []int, limit, offset int) (objs []*ResourceRds, err error) {
	err = DB.Limit(limit).Offset(offset).Find(&objs, ids).Error
	return
}

// GetResourceRdsByInstanceId 获取指定instanceId的RDS资源
func GetResourceRdsByInstanceId(instanceId string) (*ResourceRds, error) {
	var dbObj ResourceRds
	err := DB.Where("instance_id = ?", instanceId).Preload("BindNodes").First(&dbObj).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("RDS: %s not found")
		}
		return nil, fmt.Errorf("Database query failed: %s error", err.Error())
	}
	return &dbObj, nil
}

// GetResourceRdsById 获取指定id的RDS资源
func GetResourceRdsById(id int) (*ResourceRds, error) {
	var dbObj ResourceRds
	err := DB.Where("id = ?", id).Preload("BindNodes").First(&dbObj).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("RDS: %d not found")
		}
		return nil, fmt.Errorf("Database query failed: %s error", err.Error())
	}
	return &dbObj, nil
}

// GetResourceRdsUidAndHash 获取所有RDS的uid和hash
func GetResourceRdsUidAndHash() (map[string]string, error) {
	var objs []*ResourceRds
	err := DB.Find(&objs).Error
	if err != nil {
		return nil, err
	}
	m := make(map[string]string)
	for _, h := range objs {
		m[h.InstanceId] = h.Hash
	}
	return m, nil
}

// 获取特定服务树节点绑定的资源列表， 给删除叶子节点时做校验如果绑定了资源，则不允许删除
func GetResourceRdsBoundToNode(nodeID uint) ([]*ResourceRds, error) {
	allRds, err := GetResourceRdsAll()
	if err != nil {
		return nil, err
	}

	var boundResources []*ResourceRds
	for _, rds := range allRds {
		elb := rds
		for _, bindNode := range elb.BindNodes {
			if bindNode.ID == nodeID {
				boundResources = append(boundResources, rds)
				break
			}
		}
	}
	return boundResources, nil
}
